package battleship_client;

import battleship_admin.AdminClientThread;
import battleship_client.screens.GameOrSettingsScreen;
import battleship_client.screens.SelectScreen;
import battleship_client.screens.ErrorScreen;
import battleship_client.screens.InitScreen;
import battleship_client.screens.GameScreen;
import battleship_client.screens.DataScreen;
import battleship_client.screens.LoginScreen;
import battleship_client.screens.AdminScreen;
import battleship_client.screens.ScoresScreen;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.Date;

/**
 *
 * @author omar
 */
public class Controller {

    public Validator validator;
    private FilterManager filterManager;
    private LoginScreen loginScreen;
    private DataScreen dataScreen;
    private GameOrSettingsScreen gameOrSettingsScreen;
    private SelectScreen selectScreen;
    private GameScreen gameScreen;
    private InitScreen initScreen;
    private AdminScreen adminScreenInit;
    private ScoresScreen scoresScreen;
    private boolean connectionSuccessed = false;
    private static ScreenThread thread = null;
    private static Controller controller;
    private AdminClientThread act = null;
    private boolean scores = false;

    /**
     * Crea un objeto Controller
     */
    public Controller() {
        initScreen = new InitScreen(this);
        initScreen.setVisible(true);
        filterManager = new FilterManager(this);
    }

    public static Controller getInstance() {
        if (controller == null) {
            controller = new Controller();
        }
        return controller;
    }

    /**
     * Empieza la conexión con el Filter Manager
     * En caso de que se intente cuatro veces y no se encuentre el servidor,
     * se termina el proceso.
     * @throws InterruptedException
     */
    public void startConnection() throws InterruptedException {
        boolean connected = false;
        int tries = 0;
        while (!connected) {
            try {
                filterManager.startConnection();
                connected = true;
            } catch (UnknownHostException e) {
                initScreen.setText("Cannot find server...");
                Thread.sleep(3000);
                initScreen.setText("Trying again...");
                tries++;
            } catch (IOException e) {
                initScreen.setText("Cannot establish a connection with server...");
                Thread.sleep(3000);
                initScreen.setText("Trying again...");
                tries++;
            }
            if (tries == 4) {
                initScreen.setText("Could not establish a connection with server.");
                Thread.sleep(3000);
                System.exit(1);
            }
        }
        initScreen.setText("Waiting response from server...");
        Thread.sleep(2000);
        if (filterManager != null) {
            validator = new Validator(this);
            loginScreen = new LoginScreen(this);
            initScreen.setText("Connection established...");
            Thread.sleep(2000);
            initScreen.dispose();
            loginScreen.setVisible(true);
        }
    }

    public GameScreen getGSSingleton() {
        return getGameScreen();
    }

    public void setConnectionSuccessed(boolean b) {
        connectionSuccessed = b;
    }

    /**
     * Exits the application
     */
    public void exit() {
        System.exit(0);
    }

    /**
     * Maneja del lado del cliente lo que se muestra cuando se pierde
     * la conexión con el servidor.
     */
    public void lostConection() {
        loginScreen = new LoginScreen(this);
        loginScreen.setVisible(true);
        error("The connection with the server has been lost", true);
    }

    /**
     * Envía a la conexión los datos de login
     * @param usuario el nombre de usuario
     * @param contraseña su contraseña
     */
    public void login(String user, char[] password) {
        String serverResponse = filterManager.login(user, password);
        if (serverResponse.equals("true")) {
            loginScreen.dispose();
            setGameOrSettingsScreen(new GameOrSettingsScreen(this));
            getGameOrSettingsScreen().setVisible(true);
        } else {
            loginScreen.setLblErrorText(serverResponse);
        }

    }

    /*
     * Sends the info to the Admin DB
     * @param user is the username
     * @param password
     */
    public void loginAdmin(String user, char[] password) {
        if (filterManager.loginAdmin(user, password)) {
            loginScreen.dispose();
            act = new AdminClientThread(filterManager);
            act.start();

        }
    }

    /**
     * Displays the window for the creation of a new user
     */
    public void newUser() {
        loginScreen.dispose();
        dataScreen = new DataScreen(this, true);
        dataScreen.setVisible(true);
    }

    /**
     * Sends the FilterManager the data for a new user
     * @param name the user's name
     * @param username the user's username
     * @param password the user's password
     * @param email the user's email
     */
    public void newUser(String name, String username, char[] password, String email) {
        String message = filterManager.createUser(name, username, password, email);
        if (message.equals("true")) {
            dataScreen.dispose();
            setGameOrSettingsScreen(new GameOrSettingsScreen(this));
            getGameOrSettingsScreen().setVisible(true);
        } else {
            dataScreen.setLblErrorUsername(message);
        }
    }

    /**
     * Displays the window for a user to change its data
     */
    public void getData() {
        filterManager.getUserData();
        getGameOrSettingsScreen().dispose();
        dataScreen = new DataScreen(this, false);
        dataScreen.setTexts(PlayerDataStruct.getName(), PlayerDataStruct.getUsername(), PlayerDataStruct.getPassword(), PlayerDataStruct.getEmail());
        dataScreen.setVisible(true);
    }

    /**
     * Sends the FilterManager the new data of a user
     * @param name the user's name
     * @param username the user's username
     * @param password the user's password
     * @param email the user's email
     */
    public void changeData(String name, String username, char[] password, String email) {
        if (PlayerDataStruct.isDataDifferent(name, username, email, email)) {
            if (filterManager.changeUserData(name, username, password, email)) {
                dataScreen.dispose();
                setGameOrSettingsScreen(new GameOrSettingsScreen(this));
                getGameOrSettingsScreen().setVisible(true);
            } else {
                error("Oops! Something went wrong while changing the user's data.", true);
            }
        }
    }

    /**
     * Cancels the DataScreen and returns to last Screen
     */
    public void cancel(boolean newUser) {
        if (!newUser) {
            if (scores) {
                scores = false;
                scoresScreen.dispose();
                setGameOrSettingsScreen(new GameOrSettingsScreen(this));
                getGameOrSettingsScreen().setVisible(true);
            } else {
                dataScreen.dispose();
                setGameOrSettingsScreen(new GameOrSettingsScreen(this));
                getGameOrSettingsScreen().setVisible(true);
            }
        } else {
            dataScreen.dispose();
            loginScreen = new LoginScreen(this);
            loginScreen.setVisible(true);
        }
    }

    /**
     * Displays the selection Screen
     */
    public void playGame() {
        if (filterManager.readyToPlay()) {
            getGameOrSettingsScreen().dispose();
            setSelectScreen(new SelectScreen(this));
            getSelectScreen().setVisible(true);
        }
    }

    /**
     * Sends the FilterManager the selected boats
     * @param ships the array of occupied coordinates
     */
    public void selectShips(int[] ships) {
        if (filterManager.selectShips(ships)) {
            getSelectScreen().dispose();
            setGameScreen(new GameScreen(this, ships));
            getGameScreen().setVisible(true);
            getGameScreen().setAttack(false);
            FilterManagerThread st = new FilterManagerThread(filterManager);
            st.start();
        }
    }

    public void attack() {
        getGameScreen().setAttack(true);
        getGameScreen().getLblError().setText("Your turn");
        getGameScreen().getLblError().setVisible(true);
    }

    /**
     * Sends the FilterManager the movement that is going to be performed
     */
    public void attack(int x, int y) {
        getGameScreen().setAttack(false);
        if (filterManager.sendAttack(x, y)) {
            getGameScreen().getTxtMessage().setText(getGameScreen().getTxtMessage().getText() + (new Date().toString()) + " - Hit!\n");
            getGameScreen().getOpponentPanel()[x][y].setBackground(Color.red);
        } else {
            getGameScreen().getTxtMessage().setText(getGameScreen().getTxtMessage().getText() + (new Date().toString()) + " - You missed!\n");
            getGameScreen().getOpponentPanel()[x][y].setBackground(Color.black);
        }
        FilterManagerThread st = new FilterManagerThread(filterManager);
        st.start();
    }

    /**
     * Receives the opponent's move
     */
    public void receiveAttack(String message, int x, int y) {
        getGameScreen().getLblError().setText("Waiting for opponent's move...");
        getGameScreen().getLblError().setVisible(true);
        if (message.equals("Hit")) {
            getGameScreen().getOwnPanel()[x][y].setBackground(Color.red);
            getGameScreen().getTxtMessage().setText(getGameScreen().getTxtMessage().getText() + (new Date().toString()) + " - Oops!That was a hit on " + (x + 1) + ", " + (y + 1) + "\n");
        } else {
            getGameScreen().getOwnPanel()[x][y].setBackground(Color.cyan);
            getGameScreen().getTxtMessage().setText(getGameScreen().getTxtMessage().getText() + (new Date().toString()) + "You opponent tried to attack you on " + (x + 1) + ", " + (y + 1) + ", but failed.\n");
        }

        FilterManagerThread st = new FilterManagerThread(filterManager);
        st.start();
    }

    public void finishGame(String message) {
        if (message.equals("You won")) {
            error("You won! :)", false);
        } else {
            error("You lost! :(", false);
        }
    }

    public String getNames() {
        String st = filterManager.getOnlineUsers();
        //adminScreenInit.changeText(st);
        System.out.println(st);
        return st;
    }

    /**
     * Receives an error and displays it
     * @param message the message to display
     */
    public void error(String message, boolean dispose) {
        new ErrorScreen(this, message, dispose).setVisible(true);
    }

    public void gameFinished() {
        getGameScreen().dispose();
        gameOrSettingsScreen = new GameOrSettingsScreen(this);
        gameOrSettingsScreen.setVisible(true);
    }

    public void showScores() {
        gameOrSettingsScreen.dispose();
        String codedScores = filterManager.getScores();
        String[] scores = codedScores.split("|");
        System.out.println("END");
        scoresScreen = new ScoresScreen(this);
        scoresScreen.getLblScore().setText("Score: " + scores[1]);
        scoresScreen.getLblGamesPlayed().setText("Games Played: " + scores[3]);
        scoresScreen.getLblGamesWon().setText("Games Won: " + scores[5]);
        scoresScreen.getLblGamesLost().setText("Games Lost: " + scores[7]);
        this.scores = true;
        scoresScreen.setVisible(true);
    }

    /**
     * @return the selectScreen
     */
    public SelectScreen getSelectScreen() {
        return selectScreen;
    }

    /**
     * @param selectScreen the selectScreen to set
     */
    public void setSelectScreen(SelectScreen selectScreen) {
        this.selectScreen = selectScreen;
    }

    /**
     * @return the gameOrSettingsScreen
     */
    public GameOrSettingsScreen getGameOrSettingsScreen() {
        return gameOrSettingsScreen;
    }

    /**
     * @param gameOrSettingsScreen the gameOrSettingsScreen to set
     */
    public void setGameOrSettingsScreen(GameOrSettingsScreen gameOrSettingsScreen) {
        this.gameOrSettingsScreen = gameOrSettingsScreen;
    }

    /**
     * @return the gameScreen
     */
    public GameScreen getGameScreen() {
        return gameScreen;
    }

    /**
     * @param gameScreen the gameScreen to set
     */
    public void setGameScreen(GameScreen gameScreen) {
        this.gameScreen = gameScreen;
    }
}
